package server.test;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import server.common.code.NettyMessageDecoder;
import server.common.code.NettyMessageEncoder;
import server.common.handler.HeartBeatReqHandler;
import server.common.handler.LoginAuthReqHandler;
import server.common.message.NettyMessageFactory;

import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;

/**
 * Created by xiangliyou on 17-9-22.
 *
 * @DESCRIPTION 客户端测试
 */
public class NettyClient {
    //客户端netty处理事件的线程组
    private NioEventLoopGroup workGroup = new NioEventLoopGroup(4);
    //与服务端连接的管道
    private Channel channel;
    //启动的参数配置
    private Bootstrap bootstrap;
    //服务端的地址
    private final InetSocketAddress mAddress;
    //断线后的重连间隔 单位s
    private static final int TRY_RECONNECTION_INTERVAL = 3;
    //发送心跳包的间隔 单位s
    private static final int SEND_HEART_BEAT_INTERVAL = 3;

    public static void main(String[] args) throws Exception {
        NettyClient client = new NettyClient("127.0.0.1", 12345);
        //初始化配置参数
        client.initClient();
        //连接到服务端,需要同步等待连接成功或者连接超时
        client.doConnect();
        client.sendData("Hello ");
        //发送测试数据
//        for (int i = 0; i < 10; i++) {
//            String str = "-------- " + i;
//            client.sendData(str);
//            System.out.println(str);
//        }
    }


    public NettyClient(final String host, final int port) {
        mAddress = new InetSocketAddress(host, port);
    }

    /***
     * 随机时间 发送数据给服务端
     * @throws Exception
     */
    private void sendData(final String msg) throws Exception {
        if (channel != null && channel.isActive()) {
            channel.writeAndFlush(NettyMessageFactory.build(msg));
        }
    }

    /**
     * 初始化客户端连接参数
     */
    public void initClient() {
        try {
            bootstrap = new Bootstrap();
            bootstrap//
                    .group(workGroup)//
                    .channel(NioSocketChannel.class)//
                    .remoteAddress(mAddress)//
                    .handler(new ClientChannelInitializer());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 事件的处理器
     */
    private class ClientChannelInitializer extends ChannelInitializer<SocketChannel> {

        @Override
        protected void initChannel(SocketChannel socketChannel) throws Exception {
            ChannelPipeline p = socketChannel.pipeline();
            //开启读写超时
            p.addLast(new IdleStateHandler(5, 5, SEND_HEART_BEAT_INTERVAL));
            //自定义的解码器 -8表示lengthAdjustment，让解码器从0开始截取字节，并且包含消息头
            p.addLast("NettyMessageDecoder", new NettyMessageDecoder(1024 * 1024, 4, 4, -8, 0));
            //自定义的编码器
            p.addLast("NettyMessageEncoder", new NettyMessageEncoder());
            //第一次进入时的握手请求
            p.addLast("LoginAuthReqHandler", new LoginAuthReqHandler());
            //心跳处理——请求,断线重连
            p.addLast("HeartBeatReqHandler", new HeartBeatReqHandler(NettyClient.this));
            //业务处理
            p.addLast("ClientHandler", new ClientHandler());
        }
    }

    /**
     * 开始连接到客户端
     */
    public synchronized void doConnect() {
        //如果已经连接成功，这次就不连接
        if (channel != null && channel.isActive()) {
            return;
        }
        ChannelFuture future = bootstrap.connect();
        //设置断线重连的监听器（断线之后10s尝试连接一次）
        future.addListener(new ConnectionListener());
        try {
            //等待连接成功后获取管道对象，在发送数据时使用
            channel = future.sync().channel();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 断开连接，释放资源
     */
    public synchronized void disConnect() {
        if (channel != null) {
            //关闭管道
            channel.closeFuture();
            channel.close();
            channel = null;
            //关闭线程组
            workGroup.shutdownGracefully();
        }
    }

    /**
     * 重连操作的监听器
     */
    private class ConnectionListener implements ChannelFutureListener {

        @Override
        public void operationComplete(ChannelFuture future) throws Exception {

            if (future.isSuccess()) {
                System.out.println("成功连接到服务端!");
            } else {
                System.out.println("连接失败， 10s后重新尝试连接");
                //重连任务
                final EventLoop eventLoop = future.channel().eventLoop();

                eventLoop.schedule(NettyClient.this::doConnect, TRY_RECONNECTION_INTERVAL, TimeUnit.SECONDS);
            }
        }
    }

}